/**
 * 共享内存示例
 *  父进程写入数据，子进程读出
 *  使用管道做父子进程间同步
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

static int pipe_fds[2];

static void init_pipe()
{
    if (pipe(pipe_fds) < 0)
    {
        perror("pipe error: ");
        exit(EXIT_FAILURE);
    }
}

static void wait_pipe()
{
    char c;
    while (1)
    {
        if (read(pipe_fds[0], &c, 1) < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            perror("read error: ");
            break;
        }

        break;
    }
}

static void notify_pipe()
{
    char c = 1;
    if (write(pipe_fds[1], &c, 1) < 0)
    {
        perror("write error: ");
    }
}

static void destroy_pipe()
{
    close(pipe_fds[0]);
    close(pipe_fds[1]);
}

int main(void)
{
    init_pipe();

    key_t key = (key_t)20;
    int shmid;
    if ((shmid = shmget(key, 100, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0)
    {
        perror("shmget error: ");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (0 > pid)
    {
        perror("fork error: ");
        exit(EXIT_FAILURE);
    }
    else if (0 < pid)
    {
        // 父进程写入数据
        int *p = shmat(shmid, NULL, 0);
        if (p == (int *)-1)
        {
            perror("shmat error: ");
            exit(EXIT_FAILURE);
        }
        *p = 100;
        *(p + 1) = 200;

        // 父进程通知子进程
        notify_pipe();

        // 关闭管道资源
        destroy_pipe();

        // 等待子进程执行完毕，回收子进程资源
        wait(0);

        shmctl(shmid, IPC_RMID, NULL);
    }
    else
    {
        // 子进程阻塞等待父进程写入数据
        wait_pipe();

        // 子进程离开阻塞后读取数据并输出
        int *p = shmat(shmid, NULL, 0);
        if (p == (int *)-1)
        {
            perror("shmat error: ");
            exit(EXIT_FAILURE);
        }
        printf("start %d\tend %d\n", *p, *(p + 1));
    }

    return 0;
}